/*********************************************************************************
Organization 					: 				Georgia Institute of Technology
  
Institute 						:				Cognitive Computing Group(CCL)
 
Authors							: 				Santiago Ontanon, Kinshuk Mishra
 												Neha Sugandh 
 												
Class							:				CBRAdaptation
 
Function						: 				The class contains the Adptation class. 
												It provides routines to adapt plans in the plan
												library according to the current game state
*********************************************************************************/

package cbr;

import java.io.PrintStream;
import java.util.LinkedList;
import java.util.List;

import planninglanguage.plans.AttackBOPlan;
import planninglanguage.plans.AttackGroundBOPlan;
import planninglanguage.plans.BasicOperatorPlan;
import planninglanguage.plans.BuildBOPlan;
import planninglanguage.plans.MoveBOPlan;
import planninglanguage.plans.MultiPlan;
import planninglanguage.plans.ParallelPlan;
import planninglanguage.plans.Plan;
import planninglanguage.plans.RepairBOPlan;
import planninglanguage.plans.ResearchBOPlan;
import planninglanguage.plans.ResourceBOPlan;
import planninglanguage.plans.ResourceLocationBOPlan;
import planninglanguage.plans.SequentialPlan;
import planninglanguage.plans.StandGroundBOPlan;
import planninglanguage.plans.StopBOPlan;
import planninglanguage.plans.SubGoalPlan;
import planninglanguage.plans.TrainBOPlan;
import planninglanguage.plans.UpgradeBOPlan;
import base.CBLILRModule;
import base.ProxyBot;
import base.WargusGameState;
import base.WargusStateImporter;
import base.WargusUnit;


public class CBRAdaptation extends CBLILRModule {
	
	public static PrintStream m_logger = null;
	 
	public static Plan adaptPlan(Plan a_p,WargusGameState a_gamestate,int playerID) throws Exception
	{
		System.out.println("CBR: adaptPlan, adapting plan: " + a_p);
		m_logger.println("Cycle " + ProxyBot.getProxyBot().getCurrCycle() + ", CBR: adaptPlan, adapting plan: " + a_p.toString());
//		m_logger.println("Map got from proxybot:\n" + a_gamestate.getMap().toStringTerrain());
		
		if (a_p instanceof ParallelPlan) {
			ParallelPlan res = new ParallelPlan(a_p.getPreCondition(),a_p.getAliveCondition(),a_p.getSuccessCondition());
			
			for(Plan p : (MultiPlan) a_p) {
				res.add(adaptPlan(p,a_gamestate,playerID));
			}
			return res;			
		} else if (a_p instanceof SequentialPlan) {
			MultiPlan res = new SequentialPlan(a_p.getPreCondition(),a_p.getAliveCondition(),a_p.getSuccessCondition());
			
			for(Plan p : (MultiPlan) a_p) {
				res.add(adaptPlan(p,a_gamestate,playerID));
			}
			return res;
		} else if (a_p instanceof SubGoalPlan) {
			SubGoalPlan res = new SubGoalPlan(((SubGoalPlan)a_p).getGoal(),a_p.getPreCondition(),a_p.getAliveCondition(),a_p.getSuccessCondition());
			if (((SubGoalPlan)a_p).getPlan() != null ) {
				res.setPlan(adaptPlan( ((SubGoalPlan)a_p).getPlan() ,a_gamestate,playerID));
			}
			return res;
		} else if (a_p instanceof BasicOperatorPlan) {
			// Adapt ID:
			List<BasicOperatorPlan> res_l = adaptPlanUnitID((BasicOperatorPlan)a_p,a_gamestate,playerID);

			System.out.println("CBR: adaptPlan, returned " + res_l.size() + " possible operators after adapting units.");
			m_logger.println("CBR: adaptPlan, returned " + res_l.size() + " possible operators after adapting units.");

			for(BasicOperatorPlan res:res_l) {
				res.setContext(a_p.getContext());
				
				// Adapt coordinates:
				BasicOperatorPlan res2 = adaptPlanCoordinates(res,a_gamestate,playerID);
				
				if (res2!=null) { 
					res2.setContext(a_p.getContext());
					return res2;
				}
				System.out.println("CBR: adaptPlan, couldn't addapt the coordinates for this unit...");
				m_logger.println("CBR: adaptPlan, couldn't addapt the coordinates for this unit...");
			}
			
			System.out.println("CBR: adaptPlan, couldn't addapt the BasicOperatorPlan...");
			m_logger.println("CBR: adaptPlan, couldn't addapt the BasicOperatorPlan...");
			
			return null;
		} else {
			return a_p;
		}
	}
	
	/*
	 * Adapt the Unit Id 
	 */
	public static List<BasicOperatorPlan> adaptPlanUnitID(BasicOperatorPlan a_p,WargusGameState a_gamestate,int playerID) throws Exception {
		
		if (a_p instanceof AttackBOPlan) {
			WargusUnit original_u = ((AttackBOPlan)a_p).getOriginalTargetUnit();
			List<WargusUnit> l_u = CBRAdaptationUnits.findMostSimilarSelfUnit(a_p.getOriginalUnit(),a_gamestate,true,true,null,false);
			List<BasicOperatorPlan> res = new LinkedList<BasicOperatorPlan>();
			
			for(WargusUnit u:l_u) {
				List<WargusUnit> l_tu = CBRAdaptationUnits.findMostSimilarTargetUnit(original_u,a_gamestate,(original_u.getPlayerID()==((AttackBOPlan)a_p).getSelfPlayerID() ? true:false),playerID,u.getLocX(),u.getLocY(),u.getRange(ProxyBot.getProxyBot().getAdvances()));
				
				for(WargusUnit tu:l_tu) {
					res.add(new AttackBOPlan( u.getUnitID(),
							 				  tu.getUnitID(),
							 				  playerID)); 
					// XXX was passing conditions here but they weren't being used... is this a bug?
				} // for
			} // for
			
			return res;
		} else if (a_p instanceof AttackGroundBOPlan) {
			List<WargusUnit> l_u = CBRAdaptationUnits.findMostSimilarSelfUnit(a_p.getOriginalUnit(),a_gamestate,true,true,null,false);
			List<BasicOperatorPlan> res = new LinkedList<BasicOperatorPlan>();
			
			for(WargusUnit u:l_u) 
				res.add(new AttackGroundBOPlan( u.getUnitID(),
											    ((AttackGroundBOPlan)a_p).getX(),((AttackGroundBOPlan)a_p).getY(),
											    a_p.getPreCondition(),a_p.getAliveCondition(),a_p.getSuccessCondition()));
			return res;				
			
		}else if (a_p instanceof BuildBOPlan) {
			List<WargusUnit> l_u = CBRAdaptationUnits.findMostSimilarSelfUnit(a_p.getOriginalUnit(),a_gamestate,false,false,null,true);			
			List<BasicOperatorPlan> res = new LinkedList<BasicOperatorPlan>();
			
			for(WargusUnit u:l_u) 
				res.add(new BuildBOPlan	( u.getUnitID(),
  									  	  ((BuildBOPlan)a_p).getX(),((BuildBOPlan)a_p).getY(),((BuildBOPlan)a_p).getBuildType(),
  									  	  a_p.getPreCondition(),a_p.getAliveCondition(),a_p.getSuccessCondition()));
			return res;
		}else if (a_p instanceof MoveBOPlan) {
			List<WargusUnit> l_u = CBRAdaptationUnits.findMostSimilarSelfUnit(a_p.getOriginalUnit(),a_gamestate,true,false,null,false);			
			List<BasicOperatorPlan> res = new LinkedList<BasicOperatorPlan>();
			
			for(WargusUnit u:l_u) 				
				res.add(new MoveBOPlan( u.getUnitID(),
										((MoveBOPlan)a_p).getX(),((MoveBOPlan)a_p).getY(),
										a_p.getPreCondition(),a_p.getAliveCondition(),a_p.getSuccessCondition()));
			return res;
		}else if (a_p instanceof RepairBOPlan) {
			WargusUnit original_u = ((AttackBOPlan)a_p).getOriginalTargetUnit();
			List<WargusUnit> l_u = CBRAdaptationUnits.findMostSimilarSelfUnit(a_p.getOriginalUnit(),a_gamestate,false,false,null,false);
			List<BasicOperatorPlan> res = new LinkedList<BasicOperatorPlan>();
			
			for(WargusUnit u:l_u) {
				List<WargusUnit> l_tu = CBRAdaptationUnits.findMostSimilarUnit(original_u,a_gamestate,false,false,null,false);
				
				for(WargusUnit tu:l_tu) {
					res.add(new RepairBOPlan( u.getUnitID(),
											  tu.getUnitID(),
											  a_p.getPreCondition(),a_p.getAliveCondition(),a_p.getSuccessCondition()));
				}
			}
			
			return res;
		}else if (a_p instanceof ResearchBOPlan) {
			List<WargusUnit> l_u = CBRAdaptationUnits.findMostSimilarSelfUnit(a_p.getOriginalUnit(),a_gamestate,false,false,null,false);
			List<BasicOperatorPlan> res = new LinkedList<BasicOperatorPlan>();
			
			for(WargusUnit u:l_u) 	
				res.add(new ResearchBOPlan( u.getUnitID(),
											  ((ResearchBOPlan)a_p).getResearch(),
											  a_p.getPreCondition(),a_p.getAliveCondition(),a_p.getSuccessCondition()));
			return res;
		}else if (a_p instanceof UpgradeBOPlan) {
			List<WargusUnit> l_u = CBRAdaptationUnits.findMostSimilarSelfUnit(a_p.getOriginalUnit(),a_gamestate,false,false,null,false);
			List<BasicOperatorPlan> res = new LinkedList<BasicOperatorPlan>();
			
			for(WargusUnit u:l_u) 
				res.add(new UpgradeBOPlan( u.getUnitID(),
										   ((UpgradeBOPlan)a_p).getUpgrade(),
										   a_p.getPreCondition(),a_p.getAliveCondition(),a_p.getSuccessCondition()));
			return res;
		}else if (a_p instanceof ResourceBOPlan) {
			List<WargusUnit> l_u = CBRAdaptationUnits.findMostSimilarSelfUnit(a_p.getOriginalUnit(),a_gamestate,true,false,null,false);
			List<BasicOperatorPlan> res = new LinkedList<BasicOperatorPlan>();
			
			for(WargusUnit u:l_u) 
				res.add(new ResourceBOPlan( u.getUnitID(),
											((ResourceBOPlan)a_p).getResourceType(),
											a_p.getPreCondition(),a_p.getAliveCondition(),a_p.getSuccessCondition()));
			return res;
		}else if (a_p instanceof ResourceLocationBOPlan) {
			List<WargusUnit> l_u = CBRAdaptationUnits.findMostSimilarSelfUnit(a_p.getOriginalUnit(),a_gamestate,true,false,null,false);
			List<BasicOperatorPlan> res = new LinkedList<BasicOperatorPlan>();
			
			for(WargusUnit u:l_u) 
				res.add(new ResourceLocationBOPlan( u.getUnitID(),
					 								((ResourceLocationBOPlan)a_p).getX(),((ResourceLocationBOPlan)a_p).getY(),
					 								a_p.getPreCondition(),a_p.getAliveCondition(),a_p.getSuccessCondition()));
			return res;
		}else if (a_p instanceof StandGroundBOPlan) {
			List<WargusUnit> l_u = CBRAdaptationUnits.findMostSimilarSelfUnit(a_p.getOriginalUnit(),a_gamestate,true,true,null,false);
			List<BasicOperatorPlan> res = new LinkedList<BasicOperatorPlan>();
			
			for(WargusUnit u:l_u) 
				res.add(new StandGroundBOPlan( u.getUnitID(),
					 						   a_p.getPreCondition(),a_p.getAliveCondition(),a_p.getSuccessCondition()));
			return res;
		}else if (a_p instanceof StopBOPlan) {
			List<WargusUnit> l_u = CBRAdaptationUnits.findMostSimilarSelfUnit(a_p.getOriginalUnit(),a_gamestate,false,false,null,false);
			List<BasicOperatorPlan> res = new LinkedList<BasicOperatorPlan>();
			
			for(WargusUnit u:l_u) 
				res.add(new StopBOPlan( u.getUnitID(),
					 					a_p.getPreCondition(),a_p.getAliveCondition(),a_p.getSuccessCondition()));
			return res;
		}else if (a_p instanceof TrainBOPlan) {
			List<WargusUnit> l_u = CBRAdaptationUnits.findMostSimilarSelfUnit(a_p.getOriginalUnit(),a_gamestate,false,false,WargusStateImporter.unitTypeToString(((TrainBOPlan)a_p).getUnitType()),false);
			List<BasicOperatorPlan> res = new LinkedList<BasicOperatorPlan>();
			
			for(WargusUnit u:l_u) 
				res.add(new TrainBOPlan( u.getUnitID(),
										 ((TrainBOPlan)a_p).getUnitType(),
										 a_p.getPreCondition(),a_p.getAliveCondition(),a_p.getSuccessCondition()));
			return res;
		} else {
			System.out.println("CBR: adaptPlanUnitID, nothing to do with plan: " + a_p.toString());
		}
		
		List<BasicOperatorPlan> tmp = new LinkedList<BasicOperatorPlan>();
		tmp.add(a_p);
		return tmp;
	}
	
	/*
	 * Adapt the Map Coordinates 
	 */
	public static BasicOperatorPlan adaptPlanCoordinates(BasicOperatorPlan a_p,WargusGameState a_gamestate,int a_playerID) throws Exception {
		if (a_p instanceof AttackGroundBOPlan) {			
				Coordinate p = CBRAdaptationCoordinates.findMostSimilarCoordinates(((AttackGroundBOPlan)a_p).getX(),((AttackGroundBOPlan)a_p).getY(),a_gamestate,a_p.getContext(),0,a_playerID);
				if (p==null) return null;
				return new AttackGroundBOPlan( ((AttackGroundBOPlan)a_p).getUnitID(),
												p.x,p.y,
												a_p.getPreCondition(),a_p.getAliveCondition(),a_p.getSuccessCondition());
		
		} else if (a_p instanceof BuildBOPlan) {
				WargusUnit u = a_gamestate.getUnit(a_p.getUnitID());
			
				System.out.println("About to adapt coordinates for BuildBOPlan for unit " + u.toBriefString() + ":");
				
				Coordinate p = CBRAdaptationCoordinates.findMostSimilarCoordinatesReachable(u.getLocX(),u.getLocY(),((BuildBOPlan)a_p).getX(),((BuildBOPlan)a_p).getY(),a_gamestate,a_p.getContext(),WargusStateImporter.unitSize(WargusStateImporter.unitTypeToString(((BuildBOPlan)a_p).getBuildType())),a_playerID);
				if (p==null) return null;
				return new BuildBOPlan( ((BuildBOPlan)a_p).getUnitID(),
												p.x,p.y,((BuildBOPlan)a_p).getBuildType(),
												a_p.getPreCondition(),a_p.getAliveCondition(),a_p.getSuccessCondition());
	
		} else if (a_p instanceof MoveBOPlan) {
				WargusUnit u = a_gamestate.getUnit(a_p.getUnitID());
			
				System.out.println("About to adapt coordinates for MoveBOPlan for unit " + u.toBriefString() + ":");

				Coordinate p = CBRAdaptationCoordinates.findMostSimilarCoordinatesReachable(u.getLocX(),u.getLocY(),((MoveBOPlan)a_p).getX(),((MoveBOPlan)a_p).getY(),a_gamestate,a_p.getContext(),0,a_playerID);
				if (p==null) return null;
				return new MoveBOPlan( ((MoveBOPlan)a_p).getUnitID(),
												p.x,p.y,
												a_p.getPreCondition(),a_p.getAliveCondition(),a_p.getSuccessCondition());

		} else if (a_p instanceof ResourceLocationBOPlan) {


				Coordinate p = CBRAdaptationCoordinates.findMostSimilarCoordinates(((ResourceLocationBOPlan)a_p).getX(),((ResourceLocationBOPlan)a_p).getY(),a_gamestate,a_p.getContext(),0,a_playerID);
				if (p==null) return null;
				return new ResourceLocationBOPlan( ((ResourceLocationBOPlan)a_p).getUnitID(),
													p.x,p.y,
													a_p.getPreCondition(),a_p.getAliveCondition(),a_p.getSuccessCondition());

		} else {
			System.out.println("CBR: adaptPlanUnitID, nothing to do with plan: " + a_p.toString());
		}

		return a_p;
	}

	/*
	public static WargusUnit findlocationTargetID(int a_x,int a_y,CaseBaseObject a_context) throws Exception
	{
		//Extract the unitid from the gamestate which has the same unittype
		for(WargusPlayer p:a_context.getOpponentPlayers()) {
			for(WargusUnit u:p.getUnits()) {
				if (a_x >= u.getLocX() &&
					a_y >= u.getLocY() &&
					a_x < u.getLocX() + WargusStateImporter.unitSize(u.getType()) &&
					a_y < u.getLocY() + WargusStateImporter.unitSize(u.getType())) {
					return u;
				}
			}
			for(WargusUnit u:p.getReferredUnits()) {
				if (a_x >= u.getLocX() &&
					a_y >= u.getLocY() &&
					a_x < u.getLocX() + WargusStateImporter.unitSize(u.getType()) &&
					a_y < u.getLocY() + WargusStateImporter.unitSize(u.getType())) {
					return u;
				}
			}
		}
		return null;
	}
	*/
}
